/*
    This source file is part of GeNa project.
    Copyright © GeNa project 2008
    For the latest info, see http://code.google.com/p/genaproject/ or  http://lumpyproject.forums-rpg.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>. */

#include <GL/glew.h>
#include <GL/gl3.h>

#include <typeinfo>

#include "effect.h"
#include "image.h"



void Effect::loadEffect(domEffect* effect)
{
    {
        domFx_newparam_common_Array & newParamArray = effect->getNewparam_array();

        for( unsigned int i=0; i < newParamArray.getCount(); i++)
        {
            domFx_newparam_commonRef domParam = newParamArray[i];

            params[domParam->getSid()] = loadParamFromData( domParam->getFx_basic_type_common(), this);
        }
    }

    domFx_profile_abstract_Array & array = effect->getFx_profile_abstract_array();

    for( unsigned int i=0; i < array.getCount(); i++)
    {
        try
        {
            domProfile_GLSL* profile = dynamic_cast<domProfile_GLSL*>((domFx_profile_abstract*)array[i]);

            domGlsl_newparam_Array & newParamArray = profile->getNewparam_array();

            for( unsigned int i=0; i < newParamArray.getCount(); i++)
            {
                domGlsl_newparamRef domParam = newParamArray[i];
                params[domParam->getSid()] = loadParamFromData( domParam->getGlsl_param_type(), this);
            }

            domProfile_GLSL::domTechnique_Array & techniqueArray = profile->getTechnique_array();

            for( unsigned int i=0; i < techniqueArray.getCount(); i++)
            {
                techniques.push_back( new Technique(this, techniqueArray[i]));
            }

        }
        catch(std::bad_cast)
        {
            continue;
        }
    }
}

Technique::Technique(Effect* parent, domProfile_GLSL::domTechniqueRef technique)
{
    m_parent = parent;

    domGlsl_newparam_Array & newParamArray = technique->getNewparam_array();

    for( unsigned int i=0; i < newParamArray.getCount(); i++)
    {
        domGlsl_newparamRef domParam = newParamArray[i];
        params[domParam->getSid()] = loadParamFromData( domParam->getGlsl_param_type(), this);
    }

    domProfile_GLSL::domTechnique::domPass_Array & array = technique->getPass_array();

    for( unsigned int i=0; i < array.getCount(); i++)
    {
        domProfile_GLSL::domTechnique::domPassRef pass = array[i];
        passes.push_back( new Pass(this, pass));
    }

}


template<class T, class U> void copy2( T to, U from)
{
    to[0] = from[0];
    to[1] = from[1];
}

template<class T, class U> void copy4( T to, U from)
{
    to[0] = from[0];
    to[1] = from[1];
    to[2] = from[2];
    to[3] = from[3];
}

Pass::Pass(Technique* parent, domProfile_GLSL::domTechnique::domPassRef pass)
{
    m_parent = parent;

    init(); //Default value

    //TODO : Load render states

    //Clear targets
    clearTargetsMax = 0;

    {
        domFx_clearcolor_common_Array & array = pass->getColor_clear_array();

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            if( array[i]->getIndex() > clearTargetsMax-1)
                clearTargetsMax = array[i]->getIndex()+1;
        }
    }

    {
        domFx_cleardepth_common_Array & array = pass->getDepth_clear_array();

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            if( array[i]->getIndex() > clearTargetsMax-1)
                clearTargetsMax = array[i]->getIndex()+1;
        }
    }

    {
        domFx_clearstencil_common_Array & array = pass->getStencil_clear_array();

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            if( array[i]->getIndex() > clearTargetsMax-1)
                clearTargetsMax = array[i]->getIndex()+1;
        }
    }

    clearTargets = new ClearTarget*[clearTargetsMax];
    for( unsigned int i=0; i < clearTargetsMax; i++)
        clearTargets[i] = NULL;

    {
        domFx_clearcolor_common_Array & array = pass->getColor_clear_array();

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            ClearTarget* & clearTarget = clearTargets[array[i]->getIndex()];
            if( ! clearTarget)
                clearTarget = new ClearTarget;

            clearTarget[i].clear |= GL_COLOR_BUFFER_BIT;

            domFx_color_common vals = array[i]->getValue();

            copy4( clearTarget[i].clearColor, vals);
        }
    }

    {
        domFx_cleardepth_common_Array & array = pass->getDepth_clear_array();

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            ClearTarget* & clearTarget = clearTargets[array[i]->getIndex()];
            if( ! clearTarget)
                clearTarget = new ClearTarget;

            clearTarget[i].clear |= GL_DEPTH_BUFFER_BIT;

            clearTarget[i].clearDepth = array[i]->getValue();
        }
    }

    {
        domFx_clearstencil_common_Array & array = pass->getStencil_clear_array();

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            ClearTarget* & clearTarget = clearTargets[array[i]->getIndex()];
            if( ! clearTarget)
                clearTarget = new ClearTarget;

            clearTarget[i].clear |= GL_STENCIL_BUFFER_BIT;

            clearTarget[i].clearStencil = array[i]->getValue();
        }
    }

    //Render states
    domGl_pipeline_settings_Array & rsArray = pass->getGl_pipeline_settings_array();

    for( unsigned int i = 0; i < rsArray.getCount(); i++)
    {
        domGl_pipeline_settingsRef renderState = rsArray[i];

        if( renderState->getAlpha_func())
        {
            rsAlphaFunc = true;
            alphaFunc = renderState->getAlpha_func()->getFunc()->getValue();
            alphaFuncValue = renderState->getAlpha_func()->getValue()->getValue();
        }

        if( renderState->getBlend_func())
        {
            rsBlendFunc = true;
            blendFuncSrc = renderState->getBlend_func()->getSrc()->getValue();
            blendFuncDest = renderState->getBlend_func()->getDest()->getValue();
        }

        if( renderState->getBlend_func_separate())
        {
            rsBlendFuncSeparate = true;
            blendFuncSrcRGB = renderState->getBlend_func_separate()->getSrc_rgb()->getValue();
            blendFuncDestRGB = renderState->getBlend_func_separate()->getDest_rgb()->getValue();
            blendFuncSrcAlpha = renderState->getBlend_func_separate()->getSrc_alpha()->getValue();
            blendFuncDestAlpha = renderState->getBlend_func_separate()->getDest_alpha()->getValue();
        }

        if( renderState->getBlend_equation())
        {
            rsBlendEqu = true;
            blendEqu = renderState->getBlend_equation()->getValue();
        }

        if( renderState->getBlend_equation_separate())
        {
            rsBlendEquSeparate = true;
            blendEquRGB = renderState->getBlend_equation_separate()->getRgb()->getValue();
            blendEquAlpha = renderState->getBlend_equation_separate()->getAlpha()->getValue();
        }

        if( renderState->getCull_face())
        {
            rsCullMode = true;
            cullMode = renderState->getCull_face()->getValue();
        }

        if( renderState->getDepth_func())
        {
            rsDepthFunc = true;
            depthFunc = renderState->getDepth_func()->getValue();
        }

        if( renderState->getFront_face())
        {
            rsFrontFace = true;
            frontFace = renderState->getFront_face()->getValue();
        }

        if( renderState->getLogic_op())
        {
            rsLogicOp = true;
            logicOp = renderState->getLogic_op()->getValue();
        }

        if( renderState->getPolygon_mode())
        {
            if( renderState->getPolygon_mode()->getFace()->getValue() == GL_FACE_TYPE_FRONT)
            {
                rsPolygonModeFront = true;
                polygonModeFront = renderState->getPolygon_mode()->getMode()->getValue();
            }
            else if( renderState->getPolygon_mode()->getFace()->getValue() == GL_FACE_TYPE_BACK)
            {
                rsPolygonModeBack = true;
                polygonModeBack = renderState->getPolygon_mode()->getMode()->getValue();
            }
            else
            {
                rsPolygonModeFrontAndBack = true;
                polygonModeFrontAndBack = renderState->getPolygon_mode()->getMode()->getValue();
            }
        }

        if( renderState->getStencil_func())
        {
            rsStencilFunc = true;
            stencilFunc = renderState->getStencil_func()->getFunc()->getValue();
            stencilRef = renderState->getStencil_func()->getRef()->getValue();
            stencilMask = renderState->getStencil_func()->getMask()->getValue();
        }

        if( renderState->getStencil_op())
        {
            rsStencilOp = true;
            stencilOpFail = renderState->getStencil_op()->getFail()->getValue();
            stencilOpZFail = renderState->getStencil_op()->getZfail()->getValue();
            stencilOpZPass = renderState->getStencil_op()->getZpass()->getValue();
        }

        if( renderState->getStencil_func_separate())
        {
            rsStencilFuncSeparate = true;
            stencilFuncFront = renderState->getStencil_func_separate()->getFront()->getValue();
            stencilFuncBack = renderState->getStencil_func_separate()->getBack()->getValue();
            stencilRefSeparate = renderState->getStencil_func_separate()->getRef()->getValue();
            stencilMaskSeparate = renderState->getStencil_func_separate()->getMask()->getValue();
        }

        if( renderState->getStencil_op_separate())
        {
            if( renderState->getStencil_op_separate()->getFace()->getValue() == GL_FACE_TYPE_FRONT)
            {
                rsStencilOpFront = true;
                stencilOpFailFront = renderState->getStencil_op_separate()->getFail()->getValue();
                stencilOpZFailFront = renderState->getStencil_op_separate()->getZfail()->getValue();
                stencilOpZPassFront = renderState->getStencil_op_separate()->getZpass()->getValue();
            }
            else if( renderState->getStencil_op_separate()->getFace()->getValue() == GL_FACE_TYPE_BACK)
            {
                rsStencilOpBack = true;
                stencilOpFailBack = renderState->getStencil_op_separate()->getFail()->getValue();
                stencilOpZFailBack = renderState->getStencil_op_separate()->getZfail()->getValue();
                stencilOpZPassBack = renderState->getStencil_op_separate()->getZpass()->getValue();
            }
            else
            {
                rsStencilOp = true;
                stencilOpFail = renderState->getStencil_op_separate()->getFail()->getValue();
                stencilOpZFail = renderState->getStencil_op_separate()->getZfail()->getValue();
                stencilOpZPass = renderState->getStencil_op_separate()->getZpass()->getValue();
            }
        }

        if( renderState->getStencil_mask_separate())
        {
            if( renderState->getStencil_mask_separate()->getFace()->getValue() == GL_FACE_TYPE_FRONT)
            {
                rsStencilMaskFront = true;
                stencilMaskFront = renderState->getStencil_mask_separate()->getMask()->getValue();
            }
            else if( renderState->getStencil_mask_separate()->getFace()->getValue() == GL_FACE_TYPE_BACK)
            {
                rsStencilMaskBack = true;
                stencilMaskBack = renderState->getStencil_mask_separate()->getMask()->getValue();
            }
            else
                stencilMaskBack = renderState->getStencil_mask_separate()->getMask()->getValue();
        }

        if( renderState->getClip_plane())
        {
            if(renderState->getClip_plane()->getIndex() > clipPlanes.size())
                clipPlanes.resize( renderState->getClip_plane()->getIndex()+1, NULL);
            double* & vals = clipPlanes[renderState->getClip_plane()->getIndex()];

            if( !vals)
                vals = new double[4];

            copy4(vals, renderState->getClip_plane()->getValue());
        }

        if( renderState->getClip_plane_enable() && renderState->getClip_plane_enable()->getValue())
        {
            if(renderState->getClip_plane_enable()->getIndex() > clipPlanes.size())
                clipPlanes.resize( renderState->getClip_plane_enable()->getIndex()+1, NULL);
            clipPlanesEnable[renderState->getClip_plane_enable()->getIndex()] = true;
        }

        if( renderState->getBlend_color())
        {
            rsBlendColor = true;
            copy4(blendColor, renderState->getBlend_color()->getValue());
        }

        if( renderState->getClear_color())
        {
            rsClearColor = true;
            copy4(clearColor, renderState->getClear_color()->getValue());
        }

        if( renderState->getClear_stencil())
        {
            rsClearStencil = true;
            clearStencil = renderState->getClear_stencil()->getValue();
        }

        if( renderState->getClear_depth())
        {
            rsClearDepth = true;
            clearDepth = renderState->getClear_depth()->getValue();
        }

        if( renderState->getColor_mask())
        {
            rsColorMask = true;
            copy4(colorMask, renderState->getColor_mask()->getValue());
        }

        if( renderState->getDepth_mask())
        {
            rsDepthMask = true;
            depthMask = renderState->getDepth_mask()->getValue();
        }

        if( renderState->getLine_stipple())
        {
            rsLineStipple = true;
            copy2(lineStipple, renderState->getLine_stipple()->getValue());
        }

        if( renderState->getLine_width())
        {
            rsLineWidth = true;
            lineWidth = renderState->getLine_width()->getValue();
        }

        if( renderState->getModel_view_matrix())
        {
            rsModelViewMatrix = true;

            for( unsigned int i=0; i < 16; i++)
                modelViewMatrix.l[i] = renderState->getModel_view_matrix()->getValue()[i];
        }

        if( renderState->getPoint_size())
        {
            rsPointSize = true;
            pointSize = renderState->getPoint_size()->getValue();
        }

        if( renderState->getPolygon_offset())
        {
            rsPolygonOffset = true;
            copy2(polygonOffset, renderState->getPolygon_offset()->getValue());
        }

        if( renderState->getProjection_matrix())
        {
            rsProjectionMatrix = true;

            for( unsigned int i=0; i < 16; i++)
                projectionMatrix.l[i] = renderState->getProjection_matrix()->getValue()[i];
        }

        if( renderState->getScissor())
        {
            rsScissor = true;
            copy4(scissor, renderState->getScissor()->getValue());
        }

        if( renderState->getStencil_mask())
        {
            rsStencilMask = true;
            stencilMask = renderState->getStencil_mask()->getValue();
        }

        if( renderState->getAlpha_test_enable() && renderState->getAlpha_test_enable()->getValue())
            rsAlphaTestEnable = true;

        if( renderState->getBlend_enable() && renderState->getBlend_enable()->getValue())
            rsBlendEnable = true;

        if( renderState->getColor_logic_op_enable() && renderState->getColor_logic_op_enable()->getValue())
            rsColorLogicOpEnable = true;

        if( renderState->getCull_face_enable() && !renderState->getCull_face_enable()->getValue())
            rsCullFaceEnable = false;

        if( renderState->getDepth_test_enable() && !renderState->getDepth_test_enable()->getValue())
            rsDepthTestEnable = false;

        if( renderState->getLine_smooth_enable() && renderState->getLine_smooth_enable()->getValue())
            rsLineSmoothEnable = true;

        if( renderState->getLine_stipple_enable() && renderState->getLine_stipple_enable()->getValue())
            rsLineStippleEnable = true;

        if( renderState->getLogic_op_enable() && renderState->getLogic_op_enable()->getValue())
            rsLogicOpEnable = true;

        if( renderState->getMultisample_enable() && renderState->getMultisample_enable()->getValue())
            rsMultisampleEnable = true;

        if( renderState->getPoint_smooth_enable() && renderState->getPoint_smooth_enable()->getValue())
            rsPointSmoothEnable = true;

        if( renderState->getPolygon_offset_fill_enable() && renderState->getPolygon_offset_fill_enable()->getValue())
            rsPolygonOffsetFillEnable = true;

        if( renderState->getPolygon_offset_line_enable() && renderState->getPolygon_offset_line_enable()->getValue())
            rsPolygonOffsetLinelEnable = true;

        if( renderState->getPolygon_offset_point_enable() && renderState->getPolygon_offset_point_enable()->getValue())
            rsPolygonOffsetPointlEnable = true;

        if( renderState->getPolygon_smooth_enable() && renderState->getPolygon_smooth_enable()->getValue())
            rsPolygonSmoothEnable = true;

        if( renderState->getPolygon_stipple_enable() && renderState->getPolygon_stipple_enable()->getValue())
            rsPolygonStippleEnable = true;

        if( renderState->getSample_alpha_to_coverage_enable() && renderState->getSample_alpha_to_coverage_enable()->getValue())
            rsSampleAlphaToCoverageEnable = true;

        if( renderState->getSample_alpha_to_one_enable() && renderState->getSample_alpha_to_one_enable()->getValue())
            rsSampleAlphaToOneEnable = true;

        if( renderState->getSample_coverage_enable() && renderState->getSample_coverage_enable()->getValue())
            rsSampleCoverageEnable = true;

        if( renderState->getScissor_test_enable() && renderState->getScissor_test_enable()->getValue())
            rsScissorTestEnable = true;

        if( renderState->getStencil_test_enable() && renderState->getStencil_test_enable()->getValue())
            rsStencilTestEnable = true;

    }

    //TODO : Load COLLADA pass/draw element

    domProfile_GLSL::domTechnique::domPass::domShader_Array & shaderArray = pass->getShader_array();

    #ifndef ARB_ASSEMBLY
    programId = glCreateProgram();
    #endif

    for( unsigned int i=0; i < shaderArray.getCount(); i++)
    {
        domProfile_GLSL::domTechnique::domPass::domShaderRef shader = shaderArray[i];

        #ifdef ARB_ASSEMBLY
        unsigned int shaderId;
        glGenProgramsARB(1, &shaderId);

        if( shader->getStage() == GLSL_PIPELINE_STAGE_VERTEXPROGRAM)
        {
            vertexProgramId = shaderId;
            glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vertexProgramId);

        }
        else
        {
            fragmentProgramId = shaderId;
            glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fragmentProgramId);
        }
        #else
        unsigned int shaderId = glCreateShader( (shader->getStage() == GLSL_PIPELINE_STAGE_VERTEXPROGRAM) ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
        #endif

        domProfile_GLSL::domTechnique* technique = (domProfile_GLSL::domTechnique*)pass->getParentElement();
        domProfile_GLSL* profil = (domProfile_GLSL*)technique->getParentElement();

        if( !loadCodeAndInclude( technique, shaderId, shader->getName()->getSource()))
            loadCodeAndInclude( profil, shaderId, shader->getName()->getSource()); //TODE : Errors management

        #ifdef ARB_ASSEMBLY
        if( GL_INVALID_OPERATION == glGetError())
        {
            const unsigned char* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
            GLint errorPos = 0;
            glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB,&errorPos);

            if( vertexProgramId)
                std::cerr << "ARB vertex program compilation error :\n";
            else
                std::cerr << "ARB fragment program compilation error :\n";
            std::cerr << "Line " << errorPos << " : " << errorString << std::endl;
        }
        #else
        glCompileShader(shaderId);

        int logLength;
        glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logLength);
        char* log = new char[logLength];

        glGetShaderInfoLog(shaderId, logLength, (GLsizei*)&logLength, log);

        std::cout << "Shader Compilation Log :\n" << log << std::endl;

        glAttachShader(programId, shaderId);
        #endif
    }

    #ifndef ARB_ASSEMBLY
    glLinkProgram(programId);
    int logLength; glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLength);
    char* log = new char[logLength];
    glGetProgramInfoLog(programId, logLength, (GLsizei*)&logLength, log);
    std::cout << "Program Linkage Log :\n" << log << std::endl;
    #endif

    //Load bind list
    for( unsigned int i=0; i < shaderArray.getCount(); i++)
    {
        domProfile_GLSL::domTechnique::domPass::domShaderRef shader = shaderArray[i];
        domProfile_GLSL::domTechnique::domPass::domShader::domBind_Array & bindArray =shader->getBind_array();

        for( unsigned int i=0; i < bindArray.getCount(); i++)
        {
            domProfile_GLSL::domTechnique::domPass::domShader::domBindRef bind = bindArray[i];

            ParamBase* param;

            if( bind->getGlsl_param_type())
                param = loadParamFromData(bind->getGlsl_param_type(), this);
            else
            {
                param = m_parent->params[ bind->getParam()->getRef()];
                if( !param)
                    param = m_parent->m_parent->params[ bind->getParam()->getRef()];
            }
            std::cout << "Bind " << bind->getSymbol() << std::endl;

            unsigned int mask = 0;
            #ifdef ARB_ASSEMBLY
            if( shader->getStage() == GLSL_PIPELINE_STAGE_FRAGMENTPROGRAM)
                mask = 1 << 31;
            #endif
            binds.push_front(std::pair<GLuint, ParamBase*>(mask | getUniformLocation(bind->getSymbol()),param));
        }
    }
}

void Pass::loadCode( std::string code, unsigned int id)
{
    std::string tmp;
    bool newLine = false;
    for( unsigned int i=0; i < code.length(); i++)
    {
        if( (code[i] != ' ' && code[i] != '\t' && (code[i] != '\n' || tmp.length())) || (tmp.length() && !newLine))
        {
            if( newLine)
            {
                tmp += '\n';
                newLine = false;
            }

            if( code[i] == '\n')
                newLine = true;
            else
                tmp += code[i];
        }
    }
    code = tmp;

    #ifdef ARB_ASSEMBLY
    if( id == vertexProgramId)
        glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, code.length(), code.c_str());
    else
        glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, code.length(), code.c_str());
    #else

    unsigned int numLines = 1;
    for(unsigned int i=0; i < code.length(); i++)
    {
        if( code[i] == '\n')
            numLines++;
    }

    char* str = const_cast<char*>(code.c_str());
    char** lines = new char*[numLines];


    lines[0] = str;
    unsigned int line = 0;
    for( unsigned int i=0; i < code.length(); i++)
    {
        if( str[i] == '\n')
        {
            str[i] = '\0';
            line++;
            if( line < numLines)
                lines[line] = str + i+1;
        }
    }

    glShaderSource(id, numLines, (const GLchar**)lines, NULL);
    #endif
}


bool SurfaceParam::loadSurfaceInitElements(domFx_surface_init_commonRef surface)
{
    if( surface->getInit_as_null())
        return false;
    else if(surface->getInit_as_target())
    {
        if( (type != GL_TEXTURE_2D) && (type != GL_TEXTURE_DEPTH))
            return false;

        glGenTextures(1, &id);

        if( !viewportRatio[0] || !viewportRatio[1])
            dynamic = true;
        else
            glTexImage2D( GL_TEXTURE_2D, 0, format, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

        return true;
    }
    else if(surface->getInit_cube())
    {
        if(type != GL_TEXTURE_CUBE_MAP)
            return false;

        domFx_surface_init_cube_commonRef cube = surface->getInit_cube();

        if( cube->getAll())
        {
            domImage* imageElem = (domImage*)cube->getAll()->getRef().getElement();

            std::string filename;
            if( imageElem->getInit_from())
                filename = imageElem->getInit_from()->getValue().path();

            Image image;

            if(image.load(filename))
                return false;

            glGenTextures(1, &id);
            glBindTexture(GL_TEXTURE_CUBE_MAP, id);
            if( !image.texImage(GL_TEXTURE_CUBE_MAP, format))
                return false;
        }
        else if( cube->getPrimary())
        {
            domFx_surface_init_cube_common::domFx_surface_init_cube_common::domPrimaryRef primary = cube->getPrimary();

            if( mipLevels || mipmapGenerate) //There should be no mipmap levels for this initialization mode.
                return false;

            domImage* imageElem = (domImage*)primary->getRef().getElement();

            std::string filename;
            if( imageElem->getInit_from())
                filename = imageElem->getInit_from()->getValue().path();

            Image image;

            if(image.load(filename))
                return false;

            domFx_surface_init_cube_common::domFx_surface_init_cube_common::domPrimary::domOrder_Array & array = primary->getOrder_array();

            if( image.imgNum < array.getCount())
                return false;

            if( image.type == GL_TEXTURE_2D)
                image.type = GL_TEXTURE_CUBE_MAP;

            glGenTextures(1, &id);
            glBindTexture(GL_TEXTURE_CUBE_MAP, id);

            for( unsigned int i=0; i < array.getCount(); i++)
            {
                image.images[i].face = getFace( array[i]->getValue());

                if( !image.texImage(GL_TEXTURE_CUBE_MAP, format))
                    return false;
            }
        }
        else
        {
            domFx_surface_init_cube_common::domFace_Array & array = cube->getFace_array();

            if( array.getCount() != 6)
                return false;

            glGenTextures(1, &id);
            glBindTexture(GL_TEXTURE_CUBE_MAP, id);

            GLenum face[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};

            for( unsigned int i=0; i < 6; i++)
            {
                domImage* imageElem = (domImage*)array[i]->getRef().getElement();

                std::string filename;
                if( imageElem->getInit_from())
                    filename = imageElem->getInit_from()->getValue().path();

                Image image;

                if(image.load(filename))
                    return false;

                if( image.type != GL_TEXTURE_2D)
                    return false;

                if( !image.texImage(face[i], format))
                    return false;
            }
        }
    }
    else if(surface->getInit_volume())
    {
        if(type != GL_TEXTURE_3D)
            return false;

        domFx_surface_init_volume_commonRef volume = surface->getInit_volume();

        if( volume->getAll())
        {
            domImage* imageElem = (domImage*)volume->getAll()->getRef().getElement();

            std::string filename;
            if( imageElem->getInit_from())
                filename = imageElem->getInit_from()->getValue().path();

            Image image;

            if(image.load(filename))
                return false;

            glGenTextures(1, &id);
            glBindTexture(GL_TEXTURE_3D, id);
            if( !image.texImage(GL_TEXTURE_3D, format))
                return false;
        }
        else
        {
            domImage* imageElem = (domImage*)volume->getPrimary()->getRef().getElement();

            std::string filename;
            if( imageElem->getInit_from())
                filename = imageElem->getInit_from()->getValue().path();

            Image image;

            if(image.load(filename))
                return false;

            glGenTextures(1, &id);
            glBindTexture(GL_TEXTURE_3D, id);
            if( !image.texImage(GL_TEXTURE_3D, format))
                return false;
        }
    }
    else if(surface->getInit_planar())
    {
        if((type != GL_TEXTURE_1D) && (type != GL_TEXTURE_2D) && (type != GL_TEXTURE_RECTANGLE) && (type != GL_TEXTURE_DEPTH))
            return false;

        domImage* imageElem = (domImage*)surface->getInit_planar()->getAll()->getRef().getElement();

        std::string filename;
        if( imageElem->getInit_from())
            filename = imageElem->getInit_from()->getValue().path();

        Image image;

        if(image.load(filename))
            return false;

        glGenTextures(1, &id);
        glBindTexture(GL_TEXTURE_3D, id);
        if( !image.texImage(GL_TEXTURE_3D, format))
            return false;
    }
    else
    {
        domFx_surface_init_from_common_Array array = surface->getInit_from_array();

        if(!array.getCount())
            return true;


        Image* images = new Image[array.getCount()];
        unsigned int maxMips = 0;

        bool useTexCoord = false;
        if( !size[0] || !size[1] || !size[2])
            useTexCoord = true;

        for( unsigned int i=0; i < array.getCount(); i++)
        {
            domFx_surface_init_from_commonRef initFrom = array[i];

            domImage* imageElem= (domImage*)initFrom->getValue().getElement();

            std::string filename;
            if( imageElem->getInit_from())
                filename = imageElem->getInit_from()->getValue().path();

            if(!images[i].load(filename))
                return false;

            if( useTexCoord && !initFrom->getMip())
            {
                size[0] = images[i].images->width;
                size[1] = images[i].images->height;
                size[2] = images[i].images->depth;
            }

            if( initFrom->getMip() > maxMips)
                maxMips = initFrom->getMip();
        }


        glGenTextures(1, &id);

        if( type == GL_TEXTURE_DEPTH)
            glBindTexture( GL_TEXTURE_2D, id);
        else
            glBindTexture( type, id);

        if( type == GL_TEXTURE_3D)
        {
            for( unsigned int i=0; i <= maxMips; i++)
                glTexImage3D(type, i, format, size[0], size[1], size[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        }

        for( unsigned int i=0; i < surface->getInit_from_array().getCount(); i++)
        {
            domFx_surface_init_from_commonRef initFrom = surface->getInit_from_array()[i];

            domImage* imageElem = (domImage*)initFrom->getValue().getElement();

            std::string filename;
            if( imageElem->getInit_from())
                filename = imageElem->getInit_from()->getValue().path();

            if( (images[i].type != GL_TEXTURE_2D) && (images[i].type != GL_TEXTURE_3D))
                return false;

            if( type == GL_TEXTURE_1D)
            {
                images[i].type = GL_TEXTURE_1D;

                if( !images[i].texImage(&images[i].images[0], GL_TEXTURE_1D, array[i]->getMip(), format))
                    return false;
            }
            else if( (type == GL_TEXTURE_2D) || (type == GL_TEXTURE_DEPTH) || (type == GL_TEXTURE_RECTANGLE))
            {
                images[i].type = GL_TEXTURE_2D;

                if( !images[i].texImage(&images[i].images[0], type, array[i]->getMip(), format))
                    return false;
            }
            else if( type == GL_TEXTURE_CUBE_MAP)
            {
                if( !images[i].texImage(&images[i].images[0], getFace( initFrom->getFace()), array[i]->getMip(), format))
                    return false;
            }
            else if( type == GL_TEXTURE_3D)
            {
                images[i].type = GL_TEXTURE_2D;

                if( !images[i].texSubImage(&images[i].images[0], GL_TEXTURE_1D, array[i]->getMip(), 0, 0, array[i]->getSlice()))
                    return false;
            }
        }
    }

    return true;
}

GLenum SurfaceParam::getFace(domFx_surface_face_enum face)
{
    switch( face)
    {
        case FX_SURFACE_FACE_ENUM_POSITIVE_X:
            return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
        case FX_SURFACE_FACE_ENUM_NEGATIVE_X:
            return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
        case FX_SURFACE_FACE_ENUM_POSITIVE_Y:
            return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
        case FX_SURFACE_FACE_ENUM_NEGATIVE_Y:
            return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
        case FX_SURFACE_FACE_ENUM_POSITIVE_Z:
            return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
        case FX_SURFACE_FACE_ENUM_NEGATIVE_Z:
            return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
        default:
            return 0;
    }
}

GLenum SamplerParam::getWrap(domFx_sampler_wrap_common wrap)
{
    switch(wrap)
    {
        case FX_SAMPLER_WRAP_COMMON_NONE:
        case FX_SAMPLER_WRAP_COMMON_WRAP:
            return GL_REPEAT;
        case FX_SAMPLER_WRAP_COMMON_MIRROR:
            return GL_MIRRORED_REPEAT;
        case FX_SAMPLER_WRAP_COMMON_CLAMP:
            return GL_CLAMP_TO_EDGE;
        case FX_SAMPLER_WRAP_COMMON_BORDER:
            return GL_CLAMP_TO_BORDER;
        default:
            return 0;
    }
}

GLenum SamplerParam::getFilter(domFx_sampler_filter_common filter)
{
    switch(filter)
    {
        case FX_SAMPLER_FILTER_COMMON_NEAREST:
            return GL_NEAREST;
        case FX_SAMPLER_FILTER_COMMON_NONE:
        case FX_SAMPLER_FILTER_COMMON_LINEAR:
            return GL_LINEAR;
        case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_NEAREST:
            return GL_NEAREST_MIPMAP_NEAREST;
        case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_NEAREST:
            return GL_LINEAR_MIPMAP_NEAREST;
        case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_LINEAR:
            return GL_NEAREST_MIPMAP_LINEAR;
        case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_LINEAR:
            return GL_LINEAR_MIPMAP_LINEAR;
        default:
            return 0;
    }
}

void Pass::setupPass()
{
    //The following COLLADA render states have been ignored : color_material, fog_mode, fog_coord_src, light_model_color_control, shade_model, light_enable, light_ambient,
    //light_diffuse, ligt_specular, light_position, light_constant_attenuation, light_linear_attenuation, light_quadratic_attenuation, light_spot_cutoff, light_spot_direction, light_spot_direction,
    //light_spot_exponent, texture{1D,2D,3D,CUBE, RECT, DEPTH}, texture{1D,2D,3D,CUBE, RECT, DEPTH}_enable, texture_env_color, texture_env_mode, depth_bounds, depth_range,
    //fog_density, fog_start, fog_end, fog_color, light_model_ambient, lighting_enable, material_ambient, material_diffuse, material_emission, material_shininess, material_specular,
    //point_distance_attenuation, point_fade_threshold_size, point_size_min, point_size_max, auto_normal_enable, color_material_enable, depth_bounds_enable, depth_clamp_enable,
    //dither_enable, fog_enable, light_model_local_viewer_enable, light_model_two_side_enable, normalize_enable, rescale_normal_enable


    #ifdef DEBUG_GL_ATTRIB
    glPushAttrib(GL_ALL_ATRIB_BITS);
    #else
    glPushAttrib( GL_COLOR_BUFFER_BIT| GL_STENCIL_BUFFER_BIT| GL_DEPTH_BUFFER_BIT|GL_POLYGON_BIT| GL_POLYGON_STIPPLE_BIT|GL_TRANSFORM_BIT|GL_POINT_BIT|GL_LINE_BIT| GL_MULTISAMPLE_BIT|GL_SCISSOR_BIT);
    #endif

    if( clearTargetsMax && clearTargets[0])
    {
        if( clearTargets[0]->clear ^ GL_COLOR_BUFFER_BIT)
            glClearColor( clearTargets[0]->clearColor[0], clearTargets[0]->clearColor[1], clearTargets[0]->clearColor[2], clearTargets[0]->clearColor[3]);

        if( clearTargets[0]->clear ^ GL_DEPTH_BUFFER_BIT)
            glClearDepth( clearTargets[0]->clearDepth);

        if( clearTargets[0]->clear ^ GL_STENCIL_BUFFER_BIT)
            glClearStencil( clearTargets[0]->clearStencil);

        glClear( clearTargets[0]->clear);
    }

    if(rsAlphaFunc)
        glAlphaFunc(alphaFunc, alphaFuncValue);

    if(rsBlendFunc)
        glBlendFunc( blendFuncSrc, blendFuncDest);

    if( rsBlendFuncSeparate)
        glBlendFuncSeparate( blendFuncSrcRGB, blendFuncDestRGB, blendFuncSrcAlpha, blendFuncDestAlpha);

    if( rsBlendEqu)
        glBlendEquation( blendEqu);

    if( rsBlendEquSeparate)
        glBlendEquationSeparate( blendEquRGB, blendEquAlpha);

    if( rsCullMode)
        glCullFace(cullMode);

    if(rsDepthFunc)
        glDepthFunc( depthFunc);

    if(rsFrontFace)
        glFrontFace( frontFace);

    if(rsLogicOp)
        glLogicOp( logicOp);

    if( rsPolygonModeFront)
        glPolygonMode( GL_FRONT, polygonModeFront);

    if( rsPolygonModeBack)
        glPolygonMode( GL_BACK, polygonModeBack);

    if( rsPolygonModeFrontAndBack)
        glPolygonMode(GL_FRONT_AND_BACK, polygonModeFrontAndBack);

    if( rsStencilFunc)
        glStencilFunc(stencilFunc, stencilRef, stencilMask);

    if( rsStencilOp)
        glStencilOp(stencilOpFail, stencilOpZFail, stencilOpZPass);

    if( rsStencilOpFront)
        glStencilOp(stencilOpFailFront, stencilOpZFailFront, stencilOpZPassFront);

    if( rsStencilOpBack)
        glStencilOp(stencilOpFailBack, stencilOpZFailBack, stencilOpZPassBack);

    if( rsStencilFuncSeparate)
    {
        if( rsStencilMaskFront)
            glStencilFuncSeparate( GL_FRONT, stencilFuncFront, stencilRefSeparate, stencilMaskFront);
        else
            glStencilFuncSeparate( GL_FRONT, stencilFuncFront, stencilRefSeparate, stencilMaskSeparate);
        if( rsStencilMaskBack)
            glStencilFuncSeparate( GL_FRONT, stencilFuncFront, stencilRefSeparate, stencilMaskBack);
        else
            glStencilFuncSeparate( GL_BACK, stencilFuncBack, stencilRefSeparate, stencilMaskSeparate);
    }

    for( unsigned int i=0; i< (clipPlanes.size() > clipPlanesEnable.size()) ? clipPlanes.size() : clipPlanesEnable.size() ; i++)
    {
        if( !clipPlanesEnable[i])
            continue;

        glEnable(GL_CLIP_PLANE0 + i);
        glClipPlane(i, clipPlanes[i]);
    }

    if(rsBlendColor)
        glBlendColor(blendColor[0], blendColor[1], blendColor[2], blendColor[3]);

    if(rsClearColor)
        glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);

    if(rsClearStencil)
        glClearStencil(clearStencil);

    if(rsClearDepth)
        glClearDepth(clearDepth);

    if( rsColorMask)
        glColorMask( colorMask[0], colorMask[1], colorMask[2], colorMask[3]);

    if( rsDepthMask)
        glDepthMask( depthMask);

    if(rsLineStipple)
        glLineStipple( lineStipple[0], lineStipple[1]);

    if(rsLineWidth)
        glLineWidth(lineWidth);

    if( rsModelViewMatrix)
    {
        glMatrixMode( GL_MODELVIEW);
        glPushMatrix();
        glLoadTransposeMatrixf( modelViewMatrix.l);
    }

    if(rsPointSize)
        glPointSize(pointSize);

    if( rsPolygonOffset)
        glPolygonOffset( polygonOffset[0], polygonOffset[1]);

    if( rsProjectionMatrix)
    {
        glMatrixMode( GL_PROJECTION);
        glPushMatrix();
        glLoadTransposeMatrixf( projectionMatrix.l);
    }

    if( rsScissor)
        glScissor( scissor[0], scissor[1], scissor[2], scissor[3]);

    if(rsStencilMask)
        glStencilMask( stencilMask);

    if(rsAlphaTestEnable)
        glEnable(GL_ALPHA_TEST);

    if( rsBlendEnable)
        glEnable(GL_BLEND);

    if( rsColorLogicOpEnable)
        glEnable(GL_COLOR_LOGIC_OP);

    if( !rsCullFaceEnable)
        glDisable(GL_CULL_FACE);

    if( !rsDepthTestEnable)
        glEnable(GL_DEPTH_TEST);

    if( rsLineSmoothEnable)
        glEnable(GL_LINE_SMOOTH);

    if( rsLineStippleEnable)
        glEnable(GL_LINE_STIPPLE);

    if( rsLogicOpEnable)
        glEnable(GL_LOGIC_OP);

    if( rsMultisampleEnable)
        glEnable(GL_MULTISAMPLE);

    if( rsPointSmoothEnable)
        glEnable(GL_POINT_SMOOTH);

    if( rsPolygonOffsetFillEnable)
        glEnable(GL_POLYGON_OFFSET_FILL);

    if( rsPolygonOffsetLinelEnable)
        glEnable(GL_POLYGON_OFFSET_LINE);

    if( rsPolygonOffsetPointlEnable)
        glEnable(GL_POLYGON_OFFSET_POINT);

    if( rsPolygonSmoothEnable)
        glEnable(GL_POLYGON_SMOOTH);

    if( rsPolygonStippleEnable)
        glEnable(GL_POLYGON_STIPPLE);

    if( rsSampleAlphaToCoverageEnable)
        glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);

    if( rsSampleAlphaToOneEnable)
        glEnable(GL_SAMPLE_ALPHA_TO_ONE);

    if( rsSampleCoverageEnable)
        glEnable(GL_SAMPLE_COVERAGE);

    if( rsScissorTestEnable)
        glEnable(GL_SCISSOR_TEST);

    if( rsStencilTestEnable)
        glEnable(GL_STENCIL_TEST);

    #ifdef ARB_ASSEMBLY

    glEnable(GL_VERTEX_PROGRAM_ARB);
    glEnable(GL_FRAGMENT_PROGRAM_ARB);

    glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vertexProgramId);
    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fragmentProgramId);
    #else
    glUseProgram(programId);
    #endif

    SamplerParam::textureUnit = 0; //Reset texture unit number
    for( std::list< std::pair<GLuint, ParamBase*> >::iterator iter = binds.begin(); iter != binds.end(); iter++)
        iter->second->bind( iter->first);
}

unsigned int SamplerParam::textureUnit = 0;
